#include "mytcpsocket.h"
#include<QDebug>




MyTcpSocket::MyTcpSocket(QObject *parent)
    : QTcpSocket{parent}
{
    //在自己的类中处理信号接收，方便区分服务器接收来自不同客户端的数据
    connect(this,&QTcpSocket::readyRead,this,&MyTcpSocket::recvMsg);

    //在自己的类中处理 用户下线
    connect(this,&QTcpSocket::disconnected,this,&MyTcpSocket::clientOffline);
}

QString MyTcpSocket::getSocketName()
{
    //获得socket name
    return m_strName;
}


void MyTcpSocket::recvMsg()
{
    qDebug()<<"bytesAvailable is : "<<this->bytesAvailable();
    //处理接收的数据协议单元
    //先接收四个字节，uiPDUlen,包含了整个数据协议单元的长度

    uint uiPDUlen=0;
    //read 方法接收数据
    //qint64 read(char *data, qint64 maxlen);
    this->read((char *)&uiPDUlen,sizeof(uint));

    //数据真实长度是 数据协议总长度 - PDU结构体占用大小
    uint uiMsglen=uiPDUlen-sizeof(PDU);

    qDebug()<<"MytcpServer uiPDUlen"<<uiPDUlen;

    PDU *pdu=mkPDU(uiMsglen);

    //接收剩余的字节数据,偏移 uiPDUlen , (char *)pdu，表示pdu数据地址
    this->read((char *)pdu+sizeof(uint),uiPDUlen-sizeof(uint));

    // qDebug()<<"uiMsgType is : "<<pdu->uiMsgType<<"caMsg is : "<<(char *)(pdu->caMsg);

    //获取消息类型，和接收的 caData数据，caData数据在注册时存放了用户名和密码，字节长度为32位
    //根据不同的消息类型，对应进行操作
    switch (pdu->uiMsgType) {
    case ENUM_MSG_TYPE_REGIST_REQUEST:
    {
        // 定义用户名和密码
        char caName[32]={'\0'};
        char caPwd[32]={'\0'};

        //数据读取到 pdu 后进行拷贝
        strncpy(caName,pdu->caData,32);
        strncpy(caPwd,pdu->caData+32,32);

        //数据库操作，存储数据
        bool handleRegistRet= OperatorDB::getInstance().handleRegist(caName,caPwd);

        //产出回复的PDU
        PDU *respdu=mkPDU(0);

        respdu->uiMsgType=ENUM_MSG_TYPE_REGIST_RESPOND;

        if(handleRegistRet)
        {
            //产出回复的PDU
            strcpy(respdu->caData,REGIST_OK);
        }else
        {
            strcpy(respdu->caData,REGIST_FAILED);
        }


        qDebug()<<"respdu Msg Type : "<<respdu->uiMsgType;
        qDebug()<<"respdu caData : "<<respdu->caData;

        //消息发送回去,自身就是socket
        write((char *)respdu,respdu->uiPDUlen);
        free(respdu);
        respdu=NULL;

        break;
    }
    case ENUM_MSG_TYPE_LOGIN_REQUEST:
    {
        // 定义用户名和密码
        char caName[32]={'\0'};
        char caPwd[32]={'\0'};

        //数据读取到 pdu 后进行拷贝
        strncpy(caName,pdu->caData,32);
        strncpy(caPwd,pdu->caData+32,32);

        //数据库操作，存储数据
        bool handleLoginRet= OperatorDB::getInstance().handleLogin(caName,caPwd);

        //产出回复的PDU
        PDU *respdu=mkPDU(0);

        respdu->uiMsgType=ENUM_MSG_TYPE_LOGIN_RESPOND;

        if(handleLoginRet)
        {
            //产出回复的PDU
            strcpy(respdu->caData,LOGIN_OK);

            //记录登录状态的socket 名称
            //m_strName 定义每个 socket 的名字
            m_strName=caName;
        }else
        {
            strcpy(respdu->caData,LOGIN_FAILED);
        }

        qDebug()<<"respdu Msg Type : "<<respdu->uiMsgType;
        qDebug()<<"respdu caData : "<<respdu->caData;

        //消息发送回去,自身就是socket
        write((char *)respdu,respdu->uiPDUlen);
        free(respdu);
        respdu=NULL;

        break;
    }
    case ENUM_MSG_TYPE_ALL_ONLINE_TYPE_RESPOND:
    {

        //数据库查询，在线的用户和名字返回，socket写回客户端

        QStringList ret=OperatorDB::getInstance().handleAllOnline();

        //产生回复的数据
        uint uiMsglen= ret.size() * 32 ; // 个数 * 每个名字的长度（32字节）
        PDU *respdu =mkPDU(uiMsglen);

        respdu->uiMsgType=ENUM_MSG_TYPE_ALL_ONLINE_TYPE_RESPOND;
        //数据拷贝
        for (int i=0;i<ret.size();i++){
            //每次存放需要偏移
            memcpy((char *)(respdu->caMsg) + i*32 ,ret.at(i).toStdString().c_str(),ret.at(i).size());
        }

        //消息发送回去,自身就是socket
        write((char *)respdu,respdu->uiPDUlen);
        free(respdu);
        respdu=NULL;

        break;
    }
    default:
        break;
    }
    free(pdu);
    pdu=NULL;
}

void MyTcpSocket::clientOffline()
{
    //用户离线，自动触发 disconnected 信号
    //修改用户在线状态
    OperatorDB::getInstance().handleOffline(m_strName.toStdString().c_str());

    //删除 socket list 的记录,发送离线的信号,this 指的是自身 socket 对象
    emit offline(this);
}
